
#include "zen_font.h"
#include "zen_image.h"
#include "zen_log.h"
#include "zen_exception.h"

#include "zen_truetype.h"

namespace Zen
{

	class Font__ : public Font
	{
	public:
		std::shared_ptr<TrueType> mTrueType;

	public:
		std::shared_ptr<FontChar> LoadCharacter(uint32_t unicode, size_t height) override
		{
			try
			{
				return _getBitmap(unicode, height);
			} catch (std::exception& e)
			{
				LogE("exception: %s", e.what());
			} catch (...)
			{
				LogE("exception");
			}
			return nullptr;
		}

		std::shared_ptr<FontChar> _getBitmap(uint32_t unicode, size_t height)
		{
			if (height == 0) return nullptr;

			float scale = (float)height / m_font_info.height;

			if (scale > 2.f || scale < 0)
			{
				LogE("error font size, height:%u scale:%f", height, scale);
				return nullptr;
			}

			int g = mTrueType->GetGlyphIndex((int)unicode);

			auto c = std::shared_ptr<FontChar>(new FontChar);

			c->unicode = unicode;

			auto bitmap = mTrueType->GetGlyphBitmap(g, (float)height);
			if (bitmap.pixels.size())
			{
				c->bitmap = std::move(bitmap.pixels);

				c->width = bitmap.w;
				c->height = bitmap.h;
			}
			c->bitmap_y = bitmap.y;
			c->bitmap_x = bitmap.x;
			c->x_advance = bitmap.x_advance;
			c->y_advance = int(m_font_info.line_height);
			return c;
		}

		void initFont_(const std::vector<uint8_t>& value) /* noexcept(false) */
		{
			mTrueType->initFont(value);
			auto met = mTrueType->GetFontMetrics();
			int limit = (1 << 12);

			__zen_must_else(abs(met.ascent) < limit, "invalid font ascent");
			__zen_must_else(abs(met.lineGap) < limit, "invalid font line gap");
			__zen_must_else(met.height > 0 && met.height < limit, "invalid font height");

			m_font_info.height = met.height;
			m_font_info.ascent = met.ascent;
			m_font_info.line_height = met.lineGap + m_font_info.height;
		}

		FontMetrics const& GetFontInfo() override
		{
			return m_font_info;
		}

		Font__()
		{
			mTrueType = TrueType::Generate();
		}

	protected:
		FontMetrics m_font_info;
		bool m_is_good = false;
	};

	std::shared_ptr<Font> Font::Generate(const std::vector<uint8_t>& value)
	{
		auto font = std::shared_ptr<Font__>(new Font__);
		font->initFont_(value);
		return font;
	}

	class FontBrush__ : public FontBrush
	{
	protected:
		std::shared_ptr<Font> m_font;
		std::map<uint32_t, std::shared_ptr<FontChar>> m_chars;
		FontMetrics m_info;
		size_t m_height;

	public:
		FontBrush__(std::shared_ptr<Font> font, size_t height)
		{
			this->m_font = font;
			this->m_height = height;

			auto& finfo = font->GetFontInfo();

			auto scale = (float)height / finfo.height;
			//			m_info.scale = scale;
			m_info.height = (float)height;
			m_info.ascent = finfo.ascent * scale;
			m_info.line_height = finfo.line_height * scale;
		}
		std::shared_ptr<FontChar> GetCharBitmap(uint32_t unicode) override
		{
			auto iter = m_chars.find(unicode);
			if (iter != m_chars.end()) return iter->second;

			auto c = m_font->LoadCharacter(unicode, (size_t)m_height);
			m_chars[unicode] = c;
			return c;
		}

		std::shared_ptr<Font> GetFont() const override
		{
			return m_font;
		}

		const Zen::FontMetrics& GetBrushInfo() const override
		{
			return m_info;
		}

		size_t GetHeight() override
		{
			return m_height;
		}

		void ReleaseCache() override
		{
			m_chars.clear();
		}
	};

	std::shared_ptr<FontBrush> FontBrush::Generate(std::shared_ptr<Font> font, size_t height)
	{
		__zen_must_else(font, "invalid font(null)");
		auto fb = std::shared_ptr<FontBrush>(new FontBrush__(font, height));
		return fb;
	}

	FontCache* FontCache::Instance()
	{
		static auto single = new FontCache;
		return single;
		;
	}
} // namespace Zen

/////////////////
/// Render
/////////////////

namespace Zen
{

	/** image: grey format */
	static void sWriteChar(Image* image, FontChar* a, int x, int y)
	{
		int iw = (int)image->Width();
		int ih = (int)image->Height();

		x += a->bitmap_x; // image x
		y += a->bitmap_y;

		auto w = a->width; // used bitmap w
		auto h = a->height; // used bitmap h

		if (x + w <= 0 || x >= iw) return;
		if (y + h <= 0 || y >= ih) return;

		int ax = (x < 0) ? -x : 0; // bitmap x
		int ay = (y < 0) ? -y : 0; // bitmap y

		if (x + w >= iw) w = iw - x; // right out
		if (y + h >= ih) h = ih - y;
		w -= ax; // lefts out
		h -= ay;

		auto dst = image->Data() + iw * (y + ay) + (x + ax);
		auto src = a->bitmap.data() + ay * a->width + ax;
		for (int i = 0; i < h; ++i)
		{
			for (int i = 0; i < w; ++i)
			{
				if (src[i] > dst[i]) dst[i] = src[i];
			}
			//			::memcpy(dst, src, w);
			dst += iw;
			src += a->width;
		}
	}

	static void sWriteUnderline(Image* image, int x, int y, int w, int size)
	{
		int const iw = (int)image->Width();
		int const ih = (int)image->Height();
		int top = y;
		int bot = y + size;
		if (top > bot) std::swap(top, bot);
		if (top >= ih || bot <= 0) return; // y outside
		int lefts = x;
		int right = x + w;
		if (lefts > right) std::swap(lefts, right);
		if (lefts >= iw || right <= 0) return; // x outside
		if (lefts < 0) lefts = 0;
		if (right > iw) right = iw;
		if (top < 0) top = 0;
		if (bot >= ih) bot = ih;

		auto len = right - lefts; // >0
		uint8_t* src = image->Data() + (top * iw + lefts);
		while (top++ != bot)
		{
			std::fill(src, src + len, 0xff);
			src += iw;
		}
	}

	class FontRender__ : public FontRender
	{
	protected:
		int m_out_w = 0;
		int m_out_h = 0;
		int m_out_top = 0; // highest pixel pos.

		std::vector<LineInfo> m_lines;

		std::shared_ptr<FontBrush> mFontBrush;

		std::u32string mText;

		FontStyle mStyle;

	public:
		FontRender__(std::shared_ptr<FontBrush> font, std::u32string const& text, FontStyle const& style)
		{
			mFontBrush = font;
			mText = text;
			mStyle = style;

			this->_MakeLinesAutoBreak();

			if (m_lines.size())
			{
				int text_height = int(mFontBrush->GetBrushInfo().line_height);
				int last_top = (text_height + mStyle.LineSpace) * (int)(m_lines.size() - 1);
				if (last_top < 0)
				{
					m_out_top = last_top;
					m_out_h = text_height - m_out_top;
				}
				else
				{
					m_out_top = 0;
					m_out_h = last_top + text_height;
				}
			}
			else
				m_out_h = 0;

			if (m_out_h < 0) m_out_h = 0;
			if (m_out_w < 0) m_out_w = 0;
		}

		virtual std::shared_ptr<Image> RenderImage(size_t width, size_t height, P2i pos) override
		{
			if (width == 0) width = this->Width();
			if (height == 0) height = this->Height();

			if (width == 0 || height == 0) return nullptr;

			auto image = Image::GenerateBlank(EPixel::Grey, width, height, 0);

			_WriteImageH(image.get(), pos.x, pos.y);

			return image;
		}

		virtual size_t Width() override
		{
			return m_out_w;
		}

		virtual size_t Height() override
		{
			return m_out_h;
		}
		virtual size_t GetLineCount() override
		{
			return m_lines.size();
		}

		virtual std::vector<LineInfo> const& GetLines() override
		{
			return m_lines;
		}

	protected:
		void _NewLine(LineInfo& _line)
		{
			int width = abs(_line.right_limit - _line.lefts_limit);
			if (width > m_out_w) m_out_w = width;

			m_lines.push_back(_line);

			_line = LineInfo();
		}

		void _MakeLinesAutoBreak()
		{
			LineInfo _line;
			int x = 0;
			for (auto& i : mText)
			{
				if (i == '\n')
				{
					_NewLine(_line);
					continue;
				}
				auto ch = mFontBrush->GetCharBitmap(i);
				if (!ch)
				{
					Zen::LogE("error char %d", (int)i);
					continue;
				}

				if (_line.chars.size())
				{
					int lefts = x + mStyle.WordSpace + ch->bitmap_x;
					int right = lefts + ch->width;

					int lefts_limit = std::min(_line.lefts_limit, lefts);
					int right_limit = std::max(_line.right_limit, right);

					if (mStyle.WidthLimit && right_limit - lefts_limit > mStyle.WidthLimit)
					{
						_NewLine(_line);
					}
					else
					{
						_line.lefts_limit = lefts_limit;
						_line.right_limit = right_limit;
						x += mStyle.WordSpace + ch->x_advance;
						_line.chars.push_back(ch);
						continue;
					}
				}
				/// here _line.chars.size() == 0

				_line.lefts_limit = ch->bitmap_x;
				_line.right_limit = _line.lefts_limit + ch->width;
				x = ch->x_advance;

				_line.chars.push_back(ch);
			}
			_NewLine(_line);
		}

		struct UnderlineInfo
		{
			int x = 0, y = 0, w = 0;
		};

		void _WriteImageH(Image* image, int x_off, int y_off)
		{
			int y = int(y_off + mFontBrush->GetBrushInfo().ascent - m_out_top);

			int w = (int)image->Width();

			std::vector<UnderlineInfo> lines_pos;

			for (auto& line : m_lines)
			{
				int width = line.right_limit - line.lefts_limit;

				int line_x = int(mStyle.Alignment * float(w - width)) + x_off;
				int x = line_x - line.lefts_limit;
				for (auto i : line.chars)
				{
					sWriteChar(image, i.get(), x, y);
					x += i->x_advance + mStyle.WordSpace;
				}
				if (mStyle.UnderLineSize)
				{
					lines_pos.push_back({ line_x, y + mStyle.UnderLineShift, width });
				}

				y += int(mFontBrush->GetBrushInfo().line_height + mStyle.LineSpace);
			}
			for (auto ulInfo : lines_pos)
			{
				sWriteUnderline(image, ulInfo.x, ulInfo.y, ulInfo.w, mStyle.UnderLineSize);
			}
		}
	};

	std::shared_ptr<FontRender> FontRender::Generate(std::shared_ptr<FontBrush> brush, std::u32string const& text, FontStyle const& style)
	{
		auto me = new FontRender__(brush, text, style);
		return std::shared_ptr<FontRender>(me);
	}
} // namespace Zen
